/*   Copyright © 2009 Mark Gil Torres
 *
 *   This file is part of mtoolkit.
 *
 *   mtoolkit is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   any later version.
 *
 *   mtoolkit is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with mtoolkit.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef TERM_HPP
#define TERM_HPP

#include <string>
#include <vector>
#include "q.hpp"
#include "variable.hpp"

namespace mt {

  template <typename T> class Term;
  template <typename T> Term<T> const operator+(Term<T> const& inValue1, Term<T> const& inValue2);
  template <typename T> Term<T> const operator-(Term<T> const& inValue1, Term<T> const& inValue2);
  template <typename T> Term<T> const negative(Term<T> const& inValue);
  template <typename T> bool areLikeTerms(Term<T> const& inValue1, Term<T> const& inValue2);

  template <typename T = Q<int> >
  class Term {
    public:
      Term(void);
      Term(T const& inValue);
      Term(Variable<T> const& inValue);
      Term(T const& inValue1, Variable<T> const& inValue2);
      Term(Term<T> const& inValue);

      Term<T>& operator=(Term<T> const& inValue);

      void addVariable(Variable<T> const& inValue);

      T& getCoefficient(void);
      std::vector<Variable<T> >& getVariable(void);

      void setCoefficient(T const& inValue);
      void setVariable(Variable<T> const& inValue);
      void setVariable(std::vector<Variable<T> > const& inValue);

      friend Term<T> const operator+ <>(Term<T> const& inValue1, Term<T> const& inValue2);
      friend Term<T> const operator- <>(Term<T> const& inValue1, Term<T> const& inValue2);

      friend Term<T> const negative<>(Term<T> const& inValue);

      friend bool areLikeTerms<>(Term<T> const& inValue1, Term<T> const& inValue2);

      bool isConstant(void);
    private:
      T coef;
      std::vector<Variable<T> > var;
  };

//constructor
  template <typename T>
  Term<T>::Term(void) {
    coef = 0;
  }

  template <typename T>
  Term<T>::Term(T const& inValue) {
    coef = inValue;
  }

  template <typename T>
  Term<T>::Term(Variable<T> const& inValue) {
    coef = 1;
    var.push_back(inValue);
  }

  template <typename T>
  Term<T>::Term(T const& inValue1, Variable<T> const& inValue2) {
    coef = inValue1;
    var.push_back(inValue2);
  }

  template <typename T>
  Term<T>::Term(Term<T> const& inValue) : coef(inValue.coef), var(inValue.var) {}

//assignment operator
  template <typename T>
  Term<T>& Term<T>::operator=(Term<T> const& inValue) {
    coef = inValue.coef;
    var = inValue.var;
  }

  template <typename T>
  void Term<T>::addVariable(Variable<T> const& inValue) {
    var.push_back(inValue);
  }

//getter
  template <typename T>
  T& Term<T>::getCoefficient(void) {
    return coef;
  }

  template <typename T>
  std::vector<Variable<T> >& Term<T>::getVariable(void) {
    return var;
  }

//setter
  template <typename T>
  void Term<T>::setCoefficient(T const& inValue) {
    coef = inValue;
  }

  template <typename T>
  void Term<T>::setVariable(Variable<T> const& inValue) {
    var.push_back(inValue);
  }

  template <typename T>
  void Term<T>::setVariable(std::vector<Variable<T> > const& inValue) {
    var = inValue;
  }

//arithmetic operators
  template <typename T>
  Term<T> const operator+(Term<T> const& inValue1, Term<T> const& inValue2) {
    Term<T> temp(0);
    if (areLikeTerms(inValue1,inValue2)) {
     temp.coef = inValue1.coef + inValue2.coef;
     temp.var = inValue1.var;
    }
    return temp;
  }

//friend functions
//non-boolean functions
  template <typename T>
  Term<T> const negative(Term<T> const& inValue) {
    Term<T> temp(inValue);
    T nOne(-1);
    temp.coef = temp.coef * nOne;
    return temp;
  }

//boolean functions
  template <typename T>
  bool areLikeTerms(Term<T> const& inValue1, Term<T> const& inValue2) {
    Term<T> iv1Temp = inValue1;
    Term<T> iv2Temp = inValue2;
    std::vector<Variable<T> > ae_iv1 = inValue1.var;
    std::vector<Variable<T> > ae_iv2 = inValue2.var;
    if ((iv1Temp.isConstant()) && (iv2Temp.isConstant())) return true;
    if (ae_iv1 == ae_iv2) return true;
    return false;
  }

  template <typename T>
  bool Term<T>::isConstant(void) {
    if (var.empty() == true) return true;
    return false;
  }

}

#endif
